package org.lzuploader.controller;

import java.io.FileNotFoundException;

import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lzuploader.model.Config;
import org.lzuploader.model.Constants;
import org.lzuploader.model.Localization;
import org.lzuploader.model.Server;
import org.lzuploader.model.message.AbstractMessage;
import org.lzuploader.model.message.ErrorMessage;

/**
 * Uploads a file on a server, returns the contents of the data returned by the server and produces a status message.
 * This file automatically gets the contents of the current selected account in the main window
 * and sends them to the server. There is no need to give the data to the class.
 * 
 * @author Emmanuel Cron
 *
 */
public class NetUploader extends Thread
{
    /** The logger of this class. */
    private Log log = LogFactory.getLog(NetUploader.class);
    
    /** Localization of this class. */
    private Localization localization = Localization.getInstance();
    
    /** Configuration of the uploader. */
    private Config config = Config.getInstance();
    
    /** Login to use on the server. */
    private String iLogin;
    /** Password to use on the server. */
    private String hashedPassword;
    
    /** Last message encountered by this class. */
    private AbstractMessage lastMessage;
    
    /**
     * Status returned by the upload page.
     * These values should be parsed by an appropriate object in order
     * to retrieve human readable data.
     */
    private String returnedStatus;
    
    /** Creates a new {@link NetUploader} object.
     * 
     * @param login Login to use on the server
     * @param pwd Password to use on the server (already hashed)
     */
    public NetUploader(final String login, final String pwd)
    {
        iLogin = login;
        hashedPassword = pwd;
    }
    
    /** Runs the upload process.
     * This should not be called by the user. Use {@link #start()} instead.
     */
    @Override
    public void run()
    {
        log.trace("Building parts.");
        
        final Part[] parts1 = { 
            new StringPart("user", iLogin),
            new StringPart("pwd", hashedPassword),
        };
        
        log.trace("Getting server and sending data.");
        
        final Server server = config.getServers().get(config.getAccounts().get(config.getSelectedAccount()).getServer());
        final String result = NetConnection.sendToPage(server.getAddress(), server.getPort(), server.getPath() + Constants.NET_UPLOAD_PAGE, parts1);
        
        if (result == null)
        {
            log.debug("Returned data is null, sendToPage() failed. Saving error and returning.");
            
            if (NetConnection.hasMessage())
            {
                lastMessage = (ErrorMessage) NetConnection.getLastMessage();
            }
            
            return;
        }
        else if (!result.equals(Constants.NET_OK))
        {
            log.debug("Login/pwd not recognized or other message, saving error and returning.");
            
            lastMessage = new ErrorMessage(
                localization.getString("DIALOG_TITLE_ERROR"),
                localization.getString("UPLOADER_SEND_" + result)
            );
            
            return;
        }
        
        log.trace("Login/pwd recognized, now trying to send file on server.");
        
        log.trace("Rebuilding parts");
        
        final Part[] parts2 = {
            parts1[0],
            parts1[1],
            null,
            null,
        };
        
        try
        {
            parts2[2] = new StringPart("has_send_file", "");
            parts2[3] = new FilePart("LZProfiler", AccountsAccessBean.getFile(config.getSelectedAccount()));
        }
        catch (final FileNotFoundException fnfe)
        {
            log.error("FileNotFoundException even if size > 0 has already been found: " + fnfe.toString());
            log.trace("Saving error and returning.");
            
            lastMessage = new ErrorMessage(
                localization.getString("DIALOG_TITLE_ERROR"),
                localization.getString("DIALOG_FILE_TO_SEND_NOT_FOUND")
            );
            
            return;
        }
        
        final String result2 = NetConnection.sendToPage(server.getAddress(), server.getPort(), server.getPath() + Constants.NET_UPLOAD_PAGE, parts2);
        
        final StringBuffer control = new StringBuffer();
        
        if (result2 != null && result2.length() > 1)
        {
            control.append(result2.charAt(0));
            control.append(result2.charAt(1));
        }
        
        if (!control.toString().equals("OK"))
        {
            log.debug("File upload failed, saving error and returning.");
            
            if (NetConnection.hasMessage())
            {
                lastMessage = (ErrorMessage) NetConnection.getLastMessage();
            }
            else
            {
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("UPLOADER_SEND_" + result2)
                );
            }
            
            return;
        }
        else
        {
            returnedStatus = result2.substring(2);
        }
    }
    
    /**
     * Returns the messages sent by the roster page, if there are some.
     * @return the returned status
     */
    public String getReturnedStatus()
    {
        return returnedStatus;
    }
    
    /** 
     * Returns wether {@link NetUploader} produced a message on its last run.
     * @return <code>true</code> if the last process generated a message, <code>false</code> otherwise
     */
    public boolean hasMessage()
    {
        return lastMessage != null;
    }
    
    /** Returns the last message generated by this class.
     * A boolean with the <code>true</code> value is associated to this object if the
     * message is related to a post upload event, and thus that it means
     * that the process should not be retried (~ all window should be closed).
     * @return An {@link AbstractMessage} object, or <code>null</code> if none exists.
     */
    public AbstractMessage getLastMessage()
    {
        return lastMessage;
    }
}
